/* Define Value Type Aliases */
/*
var types = {};
types[Ci.nsIWindowsRegKey.TYPE_NONE] = 'REG_NONE',
types[Ci.nsIWindowsRegKey.TYPE_STRING] = 'REG_SZ',
types[Ci.nsIWindowsRegKey.TYPE_BINARY] = 'REG_BINARY',
types[Ci.nsIWindowsRegKey.TYPE_INT] = 'REG_DWORD',
types[Ci.nsIWindowsRegKey.TYPE_INT64] = 'REG_QWORD'
*/
function doRestore()
{
	/* Create New File Picker */
	var fp = Components.classes["@mozilla.org/filepicker;1"]
		.createInstance(Ci.nsIFilePicker);
	fp.init(window, "Select Collection Archive", Ci.nsIFilePicker.modeOpen);

	/* Append Filters to File Picker */
	fp.appendFilter("SaveDB Collection Archive (*.sdca)", "*.sdca");
	fp.appendFilters(Ci.nsIFilePicker.filterAll);

	var res = fp.show();
	if(res == Ci.nsIFilePicker.returnOK)
	{
		/* Get FilePicker File */
		log('Selected Collection Archive: ' + fp.fileURL.spec, SD_LOG_DEBUG2);
		var file = fp.fileURL.spec;

		var background = Cc["@mozilla.org/thread-manager;1"]
			.getService()
			.newThread(0);

		/* Dispatch GameList Thread */
		log('Dispatching Restore thread.', SD_LOG_DEBUG2);
		background.dispatch(new restoreThread(file, fp.file.path), Ci.nsIThread.DISPATCH_NORMAL);
	}
}

/* Create Restore Thread Singleton */
var restoreThread = function(file, path)
{
	this.file = file;
	this.path = path;

	this.start = new Date().getTime();
	this.games = new Array();
	this.count = 0;
	this.total = 0;

	notifyObservers(null, 'restore-begin', this.path);
};

restoreThread.prototype = {
	run: function()
	{
		notifyObservers(null, 'status-changed', 'Restoring collection...');

		log('Restoring collection.', SD_LOG_INFO);

		/* Set Default Temp Setting */
		var tempRemove = gPrefs['temp.remove'];

		/* Get Temp Directory */
		try
		{
			var tmpDir = Cc["@mozilla.org/file/directory_service;1"]
				.getService(Ci.nsIProperties)
				.get('TmpD', Ci.nsIFile);
			tmpDir.append('sDB');
		}
		catch(ex)
		{
			notifyObservers(null, 'restore-failed', 'Unable to get temp directory.');
			return;
		}

		log('Temp Directory: ' + tmpDir.path, SD_LOG_DEBUG2);
	
		/* Create Collection Temp Directory */
		try
		{
			var collectionDir = tmpDir.clone();
			collectionDir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0777);
		}
		catch(ex)
		{
			notifyObservers(null, 'restore-failed', 'Unable to create collection temp directory.');
			return;
		}	

		log('Collection Directory: ' + collectionDir.path, SD_LOG_DEBUG2);

		/* Get Zip Reader */
		try
		{
			var zpCollection = Cc["@mozilla.org/libjar/zip-reader;1"]
				.createInstance(Ci.nsIZipReader);

			zpCollection.open(this.file);

			/* Test CRC Integrity of Archive */
			zpCollection.test(null);
		}
		catch(ex)
		{
			notifyObservers(null, 'restore-failed', 'Unable to open collection archive file.');
			return;
		}

		/* Create Directory Structure in Collection Temp Directory */
		var entries = zpCollection.findEntries('/');
		while(entries.hasMore())
		{
			var entryName = entries.getNext();
			log('Entry Name: ' + entryName, SD_LOG_INFO); //todo change to debug level

			try {
				var target = this._getZipItemPath(entryName);
				if(!target.exists())
				{
					target.create(Ci.nsILocalFile.DIRECTORY_TYPE, 0777);
				}

			}
			catch(ex)
			{
				log('Unable to create collection directory structure.', SD_LOG_WARN);
				continue;
			}
		}

		/* Extract Files to Collection Temp Directory */
		var entries = zpCollection.findEntries(null);
		while(entries.hasMore())
		{
			var entryName = entries.getNext();

			try {
				var target = this._getZipItemPath(entryName);
				if(target.exists())
				{
					continue;
				}

				target.create(Ci.nsILocalFile.DIRECTORY_TYPE, 0777);
				zpCollection.extract(entryName, target);
			}
			catch(ex)
			{
				log('Unable to create collection directory structure.', SD_LOG_WARN);
				continue;
			}
		}

		zpCollection.close();

		var duration = '';
		var milliseconds = new Date().getTime() - this.start;
		var seconds = Math.ceil((milliseconds / PR_USEC_PER_MSEC));
		var minutes = 0;
		var hours = 0;
	
		if(seconds > 60)
		{
			minutes = (seconds - (seconds % 60)) / 60;
			seconds = seconds % 60;
		}
	
		if(minutes > 60)
		{
			hours = (minutes - (minutes % 60)) / 60;
			minutes = minutes % 60;
		}
	
		if(hours != 0)
		{
			duration += hours + 'h';
		}

		if(minutes != 0)
		{
			duration += minutes + 'm';
		}
	
		if(seconds != 0)
		{
			duration += seconds + 's';
		}

		notifyObservers(null, 'restore-success', duration);
	},
	QueryInterface: function(iid)
	{
		if(iid.equals(Ci.nsIRunnable) || iid.equals(Ci.nsISupports))
		{
			return this;
		}
		throw Cr.NS_ERROR_NO_INTERFACE;
	},
	_getFiles: function(entry)
	{
		log('getFiles: ' + entry.path, SD_LOG_DEBUG3);
		var fileList = new Array();
	
		/* Iterate through Directory Entries */
		if(entry.isDirectory())
		{
			var files = entry.directoryEntries;
			while(files.hasMoreElements())
			{
				var file = files.getNext();
	 			file.QueryInterface(Ci.nsIFile);
				fileList = fileList.concat(this._getFiles(file));
			}
		}
	
		/* Add File nsiFile Object to Array */
		if(entry.isFile())
		{
			fileList.push(entry);
		}
	
		return fileList;
	},
	_getRegKeys: function(wrkObj)
	{
		log('getRegKeys: ' + wrkObj.hive + '\\' + wrkObj.key, SD_LOG_DEBUG3);
		var regList = new Array();
		
		/* Iterate through Registry Key Children */
		if(wrkObj.entry.childCount > 0)
		{
			for(var i = 0; i < wrkObj.entry.childCount; i++)
			{
				var childName = wrkObj.entry.getChildName(i);
				var childEntry = wrkObj.entry.openChild(childName, Ci.nsIWindowsRegKey.ACCESS_READ);
	
				/* Create Registry Object */
				var childWrkObj = {
					entry: childEntry,
					hive: wrkObj.hive,
					key: wrkObj.key + '\\' + childName
				}
	
				/* Process Children Keys */
				regList = regList.concat(this._getRegKeys(childWrkObj));
				childEntry.close();
			}
		}
		
		/* Add Registry Values to Array */
		if(wrkObj.entry.valueCount > 0)
		{
			for(var i = 0; i < wrkObj.entry.valueCount; i++)
			{
				var valueName = wrkObj.entry.getValueName(i);
				var valueType = wrkObj.entry.getValueType(valueName);
				var valueValue = readRegistryValue(wrkObj.entry, valueName);
	
				var valueObj =  {
					hive: wrkObj.hive,
					key: wrkObj.key,
					name: valueName,
					type: valueType,
					value: valueValue
				}
					
				regList.push(valueObj);
			}
		}
	
		return regList;
	},
	_getZipItemPath: function(entryName)
	{
		try
		{
			var path = collectionDir.clone();
			var parts = entryName.split('/');
			for(var i = 0; i < parts.length; i++)
			{
				path.append(parts[i]);
			}
			return path;
		}
		catch(ex)
		{
			log('Unable to get path in archive.', SD_LOG_DEBUG1);
			return collectionDir;
		}
	}
};
